﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Interpreter
{
	internal class LocalVarBucket
	{
		public int Key;
		public Value Value;
		public LocalVarBucket Next;

		public LocalVarBucket(int key, Value value, LocalVarBucket next)
		{
			this.Key = key;
			this.Value = value;
			this.Next = next;
		}
	}

	internal class StackContext
	{
		public Value[] ValueStack;
		public int ValueStackVirtualSize;
		public int ValueStackRealSize;
		public int ProgramCounter;
		public Value ReturnValue;
		public Value ClassContext;
		public LocalVarBucket[] LocalVariables;
		public Value[] Args;
		public bool ReturnContextInstead = false;
		public bool ReturnNothing = false;
		public StackContext Below = null;
		public TryContext TryContext = null;
		public StackTrace StackTrace = null;
		public int SuddenQuit = 0; // 0 - none, 1 - uncaught exception has reached bottom of stack, 2 - uncaught exception has reached a native transition point
		public Metadata Metadata;
		public List<Value> VerifiableArgs = null;

		// True if the interpreter should stop and return after this stack context popped?
		// This is used when a native function is blocking and has a handler to run user code,
		// but we don't want the inner interpreter to keep popping up the stack.
		public bool InterpreterYieldUponPop = false; 

		public StackContext(StackContext below, int programCounter, Value classContext, Value[] args, Metadata metadata)
		{
			this.Below = below;
			this.ValueStackRealSize = Interpreter.VALUE_STACK_STARTING_ALLOCATION_SIZE;
			this.ValueStack = new Value[this.ValueStackRealSize];
			this.ValueStackVirtualSize = 0;
			this.ProgramCounter = programCounter;
			this.ReturnValue = null;
			this.ClassContext = classContext;
			this.LocalVariables = new LocalVarBucket[32];
			this.Args = args;
			this.Metadata = metadata;
		}

		private static readonly Value[] EMPTY_VALUE_ARRAY = new Value[0];
		private Value[] verifiableArgsFlattened = null;
		public Value[] GetVerifiableArgs()
		{
			if (verifiableArgsFlattened == null)
			{
				this.verifiableArgsFlattened = this.VerifiableArgs == null
					? EMPTY_VALUE_ARRAY
					: this.VerifiableArgs.ToArray();
			}
			return this.verifiableArgsFlattened;
		}

		public void AddVerifiableArg(Value value)
		{
			if (this.VerifiableArgs == null)
			{
				this.VerifiableArgs = new List<Value>();
			}
			this.VerifiableArgs.Add(value);
		}
	}
}
